package call

import (
	"dmsv/agent"
	"dmsv/common"
	"dmsv/fclient"
	"github.com/beego/beego/v2/core/logs"
	"github.com/g2fly99/esl"
	"strings"
	"time"
)

type EVT_INFO struct {
	Name   string
	EvTime time.Time
	Data   interface{}
}

type cmd_info struct {
	cName string
	data  interface{}
}

type timer_info struct {
	cmdName string
	data    interface{}
}

func (c *CallInfo) errHangUp(reason interface{}) {

	if c.IsEnd() {
		return
	}

	if reason != nil {
		c.HangUpReason = reason
	}

	err := fclient.Hangup(c.fCallId)
	if err != nil {
		logs.Error("[%v][%v] hangup by self:%v", c.sessionId, c.fCallId, err)
		return
	}

	logs.Debug("%v hang up with:%v", c.GetSessionId(), reason)

	return
}

func (c *CallInfo) startTimer(duration int, cmdName string) {

	evt := timer_info{
		cmdName: cmdName,
	}

	logs.Debug("[%v]启动定时器,超时时间:%vs,指令:%v", c.sessionId, duration, cmdName)
	time.AfterFunc(time.Duration(duration)*time.Second, func() {
		c.msgChan <- evt
	})
}

func (c *CallInfo) eventPlayOver(ev *esl.Event) {

	media := ev.Get("Playback-File-Path")
	logs.Debug("[%v] 放音结束,当前指令:%v,指令参数:%v,放音完成文件:%v.", c.sessionId, c.cmdCurr, c.cmdData, media)

	c.isPlayIng = false

	switch c.cmdCurr {
	case call_cmd_dtmf:
		//处于DTMF按键获取中，放音完成后开始按键计时
		args, ok := c.cmdData.(DTMF_ARGS)
		if !ok {
			logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
			return
		}

		c.startTimer(args.TimeOut, call_cmd_dtmf)
	case call_cmd_dtmf_resp:

	case call_cmd_play:
		msg := CallEvent{
			Name:   C_EVENT_PLAYOVER,
			EvTime: time.Now(),
			Call:   c,
			Data:   c.Dtmf,
		}

		//发往session
		c.ssChan <- msg

	case call_cmd_play_hang:

		//确保指令是正确的，有可能按键后，也会携带当前的放音
		playing, ok := c.cmdData.(string)
		if !ok {
			break
		}

		if media == playing {
			c.ErrHangUp(nil)
		}

		//发送放音结束通知
	case call_cmd_ivr_play:
		logs.Debug("[%v] 放音结束，按键:%v", c.sessionId, c.Dtmf)
		//ivr放音
		c.ivrRun("")
	case call_cmd_ivr_dtmf:
		//处于DTMF按键获取中，放音完成后开始按键计时
		args, ok := c.cmdData.(DTMF_ARGS)
		if !ok {
			logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
			return
		}
		c.startTimer(args.TimeOut, call_cmd_ivr_dtmf)
	case call_cmd_satisfaction:
		//满意度调查
	case call_cmd_play_wait:
		//等待音
		args, ok := c.cmdData.(*WaitInfo)
		if !ok {
			logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
			return
		}

		if media != args.Media && media != args.SecMedia {
			logs.Debug("[%v] 放音结束文件，与当前指令不一致，忽略消息", c.GetSessionId())
			return
		}

		args.PlayTimes--
		if args.PlayTimes >= 0 {
			c.startTimer(args.Delay, call_cmd_play_wait)
		} else {
			logs.Error("%v 放音完成,挂机,剩余播放次数[%v]..", c.GetSessionId(), args.PlayTimes)
			c.ErrHangUp(common.ErrServiceNoAnswer)
		}

	case call_cmd_play_serviceId:
		//播放完serviceId,桥接坐席侧通话
	case call_cmd_leave_msg:
		//留言
		logs.Debug("[%v] 开始留言....", c.GetSessionId())
		c.LeaveMsgTime = time.Now()
		//暂时取消留言时长限制
		//c.startTimer(60, call_cmd_leave_msg)

		time.AfterFunc(2*time.Second, func() {
			msg := CallEvent{
				Name:   C_EVENT_LEAVEMSG,
				EvTime: time.Now(),
				Call:   c,
			}

			//发往session
			c.ssChan <- msg
		})

	case call_cmd_clear:

	case call_cmd_bridge:
		logs.Debug("[%v]bridge call,drop the playdone event.", c.GetSessionId())
	default:

	}
}

func (c *CallInfo) cmdRun(cmd *cmd_info) {

	logs.Debug("[%v] 用户[%v] 收到新的指令[%v]args[%v]", c.sessionId, c.IsUser(), cmd.cName, cmd.data)
	c.cmdCurr = cmd.cName
	c.cmdData = cmd.data
	switch cmd.cName {
	case call_cmd_play:
		media, ok := c.cmdData.(string)
		if !ok {
			logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
			return
		}
		err := fclient.PlayMediaFile(c.fCallId, media)
		if err != nil {
			logs.Error("[%v] 放音失败，挂机", c.GetSessionId())
			c.ErrHangUp(common.ErrPlayFile)
		}
	case call_cmd_play_hang:
		media, ok := c.cmdData.(string)
		if !ok {
			logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
			return
		}
		err := fclient.PlayMediaFile(c.fCallId, media)
		if err != nil {
			logs.Error("[%v] 放音失败，挂机", c.GetSessionId())
			c.ErrHangUp(common.ErrPlayFile)
		}

	case call_cmd_answer:
		err := fclient.Answer(c.fCallId)
		if err != nil {
			logs.Error("[%v] 接听失败: %v", c.fCallId, err)
		} else {
			logs.Debug("[%v] 接听", c.fCallId)
			c.AnswerTime = time.Now()
		}

	case call_cmd_record:
		fileName, ok := cmd.data.(string)
		if ok {
			err := fclient.Record(c.fCallId, fileName)
			if err != nil {
				logs.Error("[%v] 开启录音失败,挂机", c.sessionId)
				c.ErrHangUp(common.ErrRecord)
				return
			}
			logs.Debug("[%v] 启动录音:%v..", c.sessionId, fileName)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), fileName)
		}

	case call_cmd_play_wait:
		switch cmd.data.(type) {
		case *WaitInfo:
			args := cmd.data.(*WaitInfo)
			fclient.PlayMediaFile(c.fCallId, args.Media)
		default:
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), cmd.data)
		}
	case call_cmd_ivr_start:
		c.runIvr()
	case call_cmd_ivr_play:
		args, ok := cmd.data.(string)
		if ok {
			fclient.PlayMediaFile(c.fCallId, args)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), args)
		}
	case call_cmd_dtmf:
		args, ok := cmd.data.(DTMF_ARGS)
		if ok {
			fclient.PlayAndGetKeySimple(c.fCallId, args.MediaFile, 1)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), args)
		}
	case call_cmd_ivr_dtmf:
		args, ok := cmd.data.(DTMF_ARGS)
		if ok {
			fclient.PlayAndGetKeySimple(c.fCallId, args.MediaFile, 1)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), args)
		}

	case call_cmd_bridge:
		call, ok := cmd.data.(*CallInfo)
		if ok {
			fclient.BridgeTwoCall(c.fCallId, call.fCallId)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), call)
		}
	case call_cmd_connect:
		callId, ok := cmd.data.(string)
		if ok {
			fclient.BridgeTwoCall(c.fCallId, callId)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), cmd.data)
		}
	case call_cmd_bridge_call:

		args, ok := cmd.data.(bridgeCall)
		if ok {

			newCallId, err := fclient.NewCallId()
			if err != nil {
				logs.Error("%v 创建uuid 失败.")
				return
			}

			callInfo := createCall(newCallId, args.caller, args.callee, args.legDir)
			callInfo.BindSession(c.sessionId, c.ssChan)

			err = fclient.BridgeCall(c.fCallId, newCallId, args.caller, args.callee)
			if err != nil {
				logs.Error("%v 桥接呼叫失败:%v.", c.sessionId, err)
				return
			}
			c.AnswerTime = time.Now()
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), args)
		}

	case call_cmd_leave_msg:
		args, ok := cmd.data.(string)
		if ok {
			fclient.PlayMediaFile(c.fCallId, args)
		} else {
			logs.Error("%v 错误的指令入参:%v", c.GetSessionId(), args)
		}
	case call_cmd_stop:

		logs.Debug("%v 停止放音，当前放音:%v....", c.GetSessionId(), c.isPlayIng)
		if false == c.isPlayIng {
			return
		}

		media, ok := cmd.data.(string)
		if ok {
			if len(media) > 0 {
				if c.lastPlay != media {
					return
				}
			}
			fclient.StopPlay(c.fCallId)
		}
	case call_cmd_hangup:
		c.errHangUp(cmd.data)

	default:

	}
}

func (c *CallInfo) SetAgent(a *agent.AgentInfo) {
	c.ServiceId = a.ServiceId()
	c.agent = a
}

func (c *CallInfo) eventParse(ev *fclient.EventInfo) {

	logs.Debug("[%v] 新的消息:%v,当前指令:[%v] ,用户侧:[%v]", c.fCallId, ev.Name, c.cmdCurr, c.IsUser())
	switch ev.Name {

	case fclient.CALL_EVENT_CREATE:
		if c.cmdCurr == call_cmd_bridge_call {
			if false == c.IsUser() {
				break
			}
		}
		c.StartTime = ev.EvTime
		msg := CallEvent{
			Name:   C_EVENT_CREATE,
			Call:   c,
			EvTime: time.Now(),
		}

		c.ssChan <- msg
	case fclient.CALL_EVENT_RING:
		if c.cmdCurr == call_cmd_bridge_call {
			if false == c.IsUser() {
				break
			}
		}

		c.RingTime = ev.EvTime
		msg := CallEvent{
			Name:   C_EVENT_RING,
			Call:   c,
			EvTime: time.Now(),
		}

		c.ssChan <- msg

	case fclient.CALL_EVENT_PLAYOVER:
		args, ok := ev.Data.(*esl.Event)
		if ok {
			c.eventPlayOver(args)
		} else {
			logs.Error("%v 事件入参类型不正确:%v", c.GetSessionId(), ev.Data)
		}
	case fclient.CALL_EVENT_ANSWER:
		c.AnswerTime = ev.EvTime
		if c.cmdCurr == call_cmd_bridge_call {
			if false == c.IsUser() {
				break
			}
		}

		msg := CallEvent{
			Name:   C_EVENT_ANSWER,
			Call:   c,
			EvTime: time.Now(),
		}
		logs.Debug("[%v] [%v]接听", c.fCallId, c.legDir)
		c.ssChan <- msg
	case fclient.CALL_EVENT_KEY:
		key := ev.Data.(string)
		if c.cmdCurr == call_cmd_ivr_dtmf {
			c.ivrRun(key)
		} else {

			//获取到key
			args, ok := c.cmdData.(DTMF_ARGS)
			if !ok {
				logs.Error("[%v] eventData 类型不符合 ", c.GetSessionId())
				return
			}
			//收到按键后，也会收到因按键打断的放音的结束通知
			c.cmdCurr = call_cmd_dtmf_resp
			c.Dtmf += key
			//不需要再获取DTMF
			if strings.Contains(args.EndKey, key) ||
				len(c.Dtmf) == args.MaxKey {

				msg := CallEvent{
					Name:   C_EVENT_KEY,
					EvTime: time.Now(),
					Call:   c,
					Data:   c.Dtmf,
				}

				//发往session
				c.ssChan <- msg
				c.Dtmf = ""
			}
		}
	case fclient.CALL_EVENT_DESTROY:

		if c.EndTime.IsZero() {
			msg := c.doDestroy(ev)

			//发送消息给session
			if c.ssChan != nil {
				c.ssChan <- msg
			} else {
				logs.Error("[%v] 未绑定session ", c.sessionId)
			}
		}
		c.destroy()
	case fclient.CALL_EVENT_DISCONNECT:

		if c.EndTime.IsZero() {
			msg := c.doDestroy(ev)

			//发送消息给session
			if c.ssChan != nil {
				c.ssChan <- msg
			} else {
				logs.Error("[%v] 未绑定session ", c.sessionId)
			}
		}
	case fclient.CALL_EVENT_FAIL:
		//坐席侧的呼叫失败
		if false == c.IsUser() && c.AnswerTime.IsZero() && c.EndTime.IsZero() {
			msg := c.doCallFail(ev)

			//发送消息给session
			if c.ssChan != nil {
				c.ssChan <- msg
			} else {
				logs.Error("[%v] 未绑定session ", c.sessionId)
			}
		}
	}
}

func (c *CallInfo) IsPstn() bool {
	return c.ispstn
}

func (c *CallInfo) timerRun(info timer_info) {

	logs.Debug("[%v] 超时消息,超时指令[%v],当前指令[%v]", c.sessionId, info.cmdName, c.cmdCurr)
	switch info.cmdName {

	case call_cmd_dtmf:
		if c.cmdCurr == call_cmd_dtmf {
			//按键超时
			msg := CallEvent{
				Name:   C_EVENT_KEY,
				EvTime: time.Now(),
				Call:   c,
				Data:   c.Dtmf,
			}

			//发往session
			c.ssChan <- msg
			c.Dtmf = ""
		}
	case call_cmd_ivr_dtmf:
		//已执行新的指令。
		if c.cmdCurr == call_cmd_ivr_dtmf {
			key := c.Dtmf
			c.Dtmf = ""
			c.ivrRun(key)
		}
	case call_cmd_leave_msg:
		if c.cmdCurr == call_cmd_leave_msg {
			logs.Debug("[%v] 留言结束，挂机...", c.sessionId)
			c.errHangUp(common.SuccLeaveMsgOver)
		}
	case call_cmd_play_wait:
		if c.cmdCurr == call_cmd_play_wait {
			args, ok := c.cmdData.(*WaitInfo)
			if !ok {
				logs.Error("[%v] 等待参数错误[%v]", c.sessionId, c.cmdData)
				return
			}

			if args.PlayTimes > 0 {
				err := fclient.PlayMediaFile(c.fCallId, args.Media)
				if err != nil {
					logs.Error("[%v] 放音失败[%v],err:%v", c.sessionId, args.Media, err)
				}
			} else {
				//有第二段语音,则播放第二段语音
				if len(args.SecMedia) > 0 {
					if args.BLeaveMsg {
						//第二段语音为播放留言语音.启动留言
						c.callPlay(call_cmd_leave_msg, args.SecMedia)
						//发送给session挂断坐席
						msg := CallEvent{
							Name:   C_EVENT_TIME_OUT,
							EvTime: time.Now(),
							Call:   c,
						}
						c.ssChan <- msg
					} else {
						//第二段语音为结束语音
						logs.Debug("[%v] 播放结束语音....", c.GetSessionId())
						err := fclient.PlayMediaFile(c.fCallId, args.SecMedia)
						if err != nil {
							logs.Error("[%v] 放音失败[%v],err:%v", c.sessionId, args.Media, err)
						}
					}
				} else {
					//没有下一步操作 挂机处理
					c.ErrHangUp(common.ErrServiceNoAnswer)
				}
			}
		}
	}
}

func (c *CallInfo) doDestroy(ev *fclient.EventInfo) CallEvent {

	c.EndTime = ev.EvTime
	if !c.AnswerTime.IsZero() {
		c.CallTime = int(c.EndTime.Sub(c.AnswerTime).Seconds()) + 1
	}

	msg := CallEvent{
		Name:   C_EVENT_DESTROY,
		EvTime: ev.EvTime,
		Call:   c,
	}

	data := ev.Data.(fclient.HangUpInfo)
	if c.AnswerTime.IsZero() {
		//未接听情况下，根据信令时长计算挂机方向
		reason := common.HANG_UP_ERR
		if len(data.SipCode) > 0 && data.SipCode != "200" {
			t := time.Since(c.StartTime)
			if c.StartTime.IsZero() {
				t = 0 * time.Second
			}

			if c.ispstn {
				//PSTN 才需要走线路预测流程
				if t <= 4*time.Second {
					reason = common.HANG_UP_LINEERR
				} else if t > 4*time.Second && t < 16*time.Second {
					reason = common.HANG_UP_REFUSE
				} else {
					if c.cmdCurr == call_cmd_hangup {
						if t > 45*time.Second {
							reason = common.HANG_UP_TIMEOUT
						} else {
							reason = common.HANG_UP_CANCEL
						}
					} else {
						reason = common.HANG_UP_NOANSWER
					}
				}
			} else {
				switch data.SipCode {
				case "487":
					reason = common.HANG_UP_REFUSE
				case "480":
					reason = common.HANG_UP_REFUSE
				case "503":
					reason = common.HANG_UP_LINEERR
				default:
					if t <= 4*time.Second {
						reason = common.HANG_UP_REFUSE
					} else {
						reason = common.HANG_UP_NOANSWER
					}
				}
			}

		} else if data.SipCode == "200" {
			if time.Since(c.StartTime) > 45*time.Second {
				reason = common.HANG_UP_TIMEOUT
			} else {
				reason = common.HANG_UP_CANCEL
			}
		} else {
			reason = common.HANG_UP_NOANSWER
		}

		c.HangupSipCode = data.SipCode
		c.HangupDescribtion = data.Describtion
		if c.HangUpReason == nil {
			c.HangUpReason = reason
		}

		logs.Debug("[%v] Dir:%v 挂机原因:%v sipCode:%v,describtion:%v，time pass:%v :%v  .",
			c.fCallId, c.legDir, c.HangUpReason, data.SipCode, data.Describtion, time.Since(c.StartTime), reason)
	}

	return msg
}

func (c *CallInfo) doCallFail(ev *fclient.EventInfo) CallEvent {

	c.EndTime = ev.EvTime
	c.CallTime = 0

	msg := CallEvent{
		Name:   C_EVENT_DESTROY,
		EvTime: ev.EvTime,
		Call:   c,
	}

	c.HangUpReason = ev.Data
	logs.Debug("[%v] Dir:%v 挂机原因:%v  .", c.fCallId, c.legDir, c.HangUpReason)
	c.destroy()

	return msg
}

func (c *CallInfo) BindSession(sessionId string, ssChan chan interface{}) {

	c.sessionId = sessionId
	c.ssChan = ssChan

	gCalls.Store(c.fCallId, c)
	go c.evRunRoutine()
}

func (c *CallInfo) BindSessionOnly(sessionId string, ssChan chan interface{}) {

	c.sessionId = sessionId
	c.ssChan = ssChan

}

func (c *CallInfo) evRunRoutine() {

	c.msgChan = make(chan interface{}, 100)

	for {
		select {

		case msg, ok := <-c.msgChan:
			if !ok {
				logs.Debug("[%v][%v] msgChan is close", c.GetSessionId(), c.fCallId)
				break
			}

			switch msg.(type) {
			case *fclient.EventInfo:
				c.eventParse(msg.(*fclient.EventInfo))
			case *cmd_info:
				c.cmdRun(msg.(*cmd_info))
			case timer_info:
				c.timerRun(msg.(timer_info))
			default:
				logs.Debug("[%v]类型不符合的消息:%v", c.GetSessionId(), msg)
			}
		}
	}
}

func (c *CallInfo) callPlay(cmdType string, args interface{}) {

	cmd := &cmd_info{
		cName: cmdType,
		data:  args,
	}

	c.msgChan <- cmd
}

func (c *CallInfo) IsUser() bool {

	return c.legDir == LEG_DIR_USER
}

func (c *CallInfo) IsService() bool {

	return c.legDir == LEG_DIR_SERVICE
}

func (c *CallInfo) IsEnd() bool {

	if c.EndTime.IsZero() {
		//从fs创建成功，都认为是未结束
		if !c.StartTime.IsZero() || !c.RingTime.IsZero() || !c.AnswerTime.IsZero() {
			return false
		}

		//直拨时
		if c.AnswerTime.IsZero() && c.callDir != CALL_DIR_IN {
			return true
		} else {
			return false
		}
	} else {
		return true
	}
}

func (c *CallInfo) GetSessionId() string {

	return c.sessionId
}

func (c *CallInfo) CallId() string {

	return c.fCallId
}

//入队时间
func (c *CallInfo) GetEnQueTime() time.Time {
	return c.EnqueTime
}

//更新入队时间
func (c *CallInfo) UpdateEnQueTime(queId int, name string) {
	if c.EnqueTime.IsZero() {
		c.EnqueTime = time.Now()
	}
	c.queueName = name
	c.QueId = queId
}

func (c *CallInfo) runIvr() {

	node, err := c.ivrHandlePtr.StartIvr()
	if err != nil {
		c.ErrHangUp(common.ErrIvrInit)
		return
	}

	media := common.MediaFullPath(c.ivrHandlePtr.AppId(), node.Play)
	if false == common.FileIsExist(media) {
		logs.Error("[%v] navigate file:%v is not exist.", c.sessionId, media)
		c.ErrHangUp(common.ErrNoFile)
		return
	}

	c.cmdCurr = call_cmd_ivr_play
	if node.GetKey {
		args := fclient.DTMF_ARGS{
			MaxKey:    1,
			MediaFile: media,
		}
		fclient.PlayAndGetKey(c.fCallId, args)
	} else {
		fclient.PlayMediaFile(c.fCallId, media)
	}
}
